#include <iostream>
#include <memory>
#include <vector>
#include <string>
#include <string>
#include <fstream>
#include <streambuf>
#include <sstream>
#include <cstdlib>
#include <new>
#include <typeinfo>
#include <list>
#include <thread> // std::thread
#include <mutex>  // std::mutex

using namespace std;

// 参考: https://blog.csdn.net/misayaaaaa/article/details/53786215
// 参考: https://github.com/applenob/Cpp_Primer_Practice/blob/master/excersize/ch13.md
// chapter 13 拷贝控制

// 1. 拷贝构造函数是什么？什么时候使用它？
// 如果一个构造函数的第一个参数是自身类类型的引用，且任何额外参数都有默认值，则此构造函数是拷贝构造函数。当使用拷贝初始化时，我们会用到拷贝构造函数

// 2.
// 解释为什么下面的声明是非法的：
// Sales_data::Sales_data(Sales_data rhs);

// 拷贝构造函数的参数必须是引用类型
// 此为一个类的拷贝构造函数，作为函数其非引用类型的参数需要进行拷贝初始化，但拷贝初始化又要调用拷贝构造函数以拷贝实参，但为了拷贝实参又需要调用拷贝构造函数，无限循环

// 3. 当我们拷贝一个StrBlob时，会发生什么？拷贝一个StrBlobPtr呢？
// StrBlob中元素复制，且智能指针计数加一。StrBlobStr中元素复制，弱指针复制不影响计数器

// 4. 假定 Point 是一个类类型，它有一个public的拷贝构造函数，指出下面程序片段中哪些地方使用了拷贝构造函数：
class Point
{
};
Point global;
Point foo_bar(Point arg)
{
    Point local = arg;
    Point *heap = new Point(global);
    *heap = local; // 拷贝赋值运算符
    Point pa[4] = {local, *heap};
    return *heap;
}

// 首先foo_bar函数的参数为非引用类型，需拷贝，使用拷贝构造函数,
// 函数的返回类型非引用，也需要进行拷贝，使用拷贝构造函数。
// 在函数体中arg拷贝到local对象，global拷贝到heap对象，local、*heap拷贝到pa[4]中皆使用拷贝构造函数
// local拷贝到*heap为拷贝赋值运算符

// 5.
// 给定下面的类框架，编写一个拷贝构造函数，拷贝所有成员。
// 你的构造函数应该动态分配一个新的string，并将对象拷贝到ps所指向的位置，而不是拷贝ps本身
class HasPtr
{
public:
    HasPtr(const string &s = string()) : ps(new string(s)), i(0) {}
    // 拷贝构造函数的申明
    HasPtr(const HasPtr &hp);
    // 拷贝运算符的申明
    HasPtr &operator=(const HasPtr &rhs);
    ~HasPtr() { delete ps; }

private:
    string *ps;
    int i;
};

// 拷贝构造函数
HasPtr::HasPtr(const HasPtr &hp)
{
    ps = new string(*hp.ps); // 拷贝ps指向的对象,而不是指针本身
    i = hp.i;
}

// 6. 拷贝赋值运算符是什么？什么时候使用它？合成拷贝赋值运算符完成什么工作？什么时候会生成合成拷贝赋值运算符？
// 拷贝复制运算符，其实就是一个名为 operator= 的函数(operator后加表示要定义的运算符的符号)，重载运算符，有返回类型和参数，返回类型通常是左侧运算符的引用
// 拷贝赋值运算符是一个名为 operator= 的函数。当赋值运算发生时就会用到它。合成拷贝赋值运算符可以用来禁止该类型对象的赋值。如果一个类未定义自己的拷贝赋值运算符，编译器会为它生成一个合成拷贝赋值运算符

// 7.
// 当我们将一个 StrBlob 赋值给另一个 StrBlob 时，会发生什么？赋值 StrBlobPtr 呢？
// 会发生浅层复制
// 所有成员的赋值会发生，两个StrBlob中智能指针所指对象内存相同，计数器加一，两个StrBlobPtr中弱指针所致对象内存相同，计数器不变

// 8.
// HasPtr 类编写赋值运算符。类似拷贝构造函数，你的赋值运算符应该将对象拷贝到ps指向的位置
// 拷贝赋值运算符
HasPtr &HasPtr::operator=(const HasPtr &rhs)
{
    auto newps = new string(*rhs.ps);
    delete ps;
    ps = newps;
    i = rhs.i;
    return *this;
}

// 9.
// 析构函数是什么？合成析构函数完成什么工作？什么时候会生成合成析构函数？
// 析构函数是类的一个成员函数，名字由波浪号接类名构成。它没有返回值，也不接受参数。
// 合成析构函数可被用来阻止该类型的对象被销毁。当一个类未定义自己的析构函数时，编译器会为它生成一个合成析构函数

// 10.
// 当一个 StrBlob 对象销毁时会发生什么？一个 StrBlobPtr 对象销毁时呢？
// 所有对象的数据成员被销毁，智能指针的计数减一，所有对象的数据成员被销毁，弱指针不影响计数器

// 11.
// 构造函数中，分配的动态内存对象，需要delete

// 12.
// 在下面的代码片段中会发生几次析构函数调用？
class Sales_data
{
public:
    int isbn(){};
};
bool fcn(const Sales_data *trans, Sales_data accum)
{
    Sales_data item1(*trans), item2(accum);
    return item1.isbn() != item2.isbn();
}

// 三次，分别是 accum、item1和item2。
// 当指向一个对象的引用或者指针离开作用域是，析构函数并不会执行

// 13.
struct X
{
    X() { std::cout << "X()" << std::endl; }
    X(const X &) { std::cout << "X(const X&)" << std::endl; }
    X &operator=(const X &)
    {
        std::cout << "X& operator=(const X&)" << std::endl;
        return *this;
    }
    ~X() { std::cout << "~X()" << std::endl; }
};

void f(const X &rx, X x)
{
    std::vector<X> vec;
    vec.reserve(2);
    vec.push_back(rx);
    vec.push_back(x);
}

// 14.
// 假定 numbered 是一个类，它有一个默认构造函数，能为每个对象生成一个唯一的序号，保存在名为 mysn 的数据成员中。假定 numbered 使用合成的拷贝控制成员，并给定如下函数：

// void f (numbered s) { cout << s.mysn < endl; }
// 则下面代码输出什么内容？

// numbered a, b = a, c = b;
// f(a); f(b); f(c);
// 解：

// 输出3个完全一样的数。
// 合成的拷贝构造函数只是简单的进行拷贝

// 15.
// 假定numbered 定义了一个拷贝构造函数，能生成一个新的序列号。这会改变上一题中调用的输出结果吗？如果会改变，为什么？新的输出结果是什么？
// 解：
// 会输出3个不同的数。并且这3个数并不是a、b、c当中的数。
// 使用了自定义版本的合成构造函数，生成新的序号，那么肯定输出就是三个不一样的数字了

// 16.

// 17.

class numbered
{
public:
    numbered()
    {
        mysn = unique++;
    }
    numbered(const numbered &n)
    {
        mysn = unique++;
    }

    int mysn;
    static int unique;
};

int numbered::unique = 10;

void f(const numbered &s)
{
    std::cout << s.mysn << std::endl;
}

// 18.
class Employee
{
public:
    Employee();
    Employee(const string &name);

    const int id() const { return id_; }

private:
    string name_;
    int id_;
    static int s_increment;
};

int Employee::s_increment = 0;
Employee::Employee()
{
    id_ = s_increment++;
}

Employee::Employee(const string &name)
{
    id_ = s_increment++;
    name_ = name;
}

// 19.
//具体操作时将类的声明置于头文件中
// delete  显式的阻止拷贝
class Employee_1
{
public:
    Employee_1();
    Employee_1(const string &name);
    Employee_1(const Employee_1 &) = delete;
    Employee_1 &operator=(const Employee_1 &) = delete;

    const int id() const { return id_; }

private:
    string name_;
    int id_;
    static int s_increment;
};

// 20.
// 解释当我们拷贝、赋值或销毁 TextQuery 和 QueryResult 类对象时会发生什么？
// 成员会被复制

// 21.
// 你认为 TextQuery 和 QueryResult 类需要定义它们自己版本的拷贝控制成员吗？如果需要，为什么？实现你认为这两个类需要的拷贝控制操作。

// 知识点：判断一个类是否需要拷贝控制函数成员，首先判断其是否需要自定义版本的析构函数，如果需要，则拷贝控制成员函数都需要。由于这两个类中的指针为智能指针，可以自动控制内存的释放，所以使用类的合成析构函数即可。
// 另外类默认的拷贝控制成员对于智能指针的拷贝也不需要自定义版本来修改，所以全部定义为 =default 即可
// 合成的版本满足所有的需求。因此不需要自定义拷贝控制成员

// 22.
<<<<<<< HEAD:c++_practice_题目/src/c++_prime_questions_chapter_13.cpp
// 假定我们希望 HasPtr 的行为像一个值。即，对于对象所指向的 string 成员，每个对象都有一份自己的拷贝。我们将在下一节介绍拷贝控制成员的定义。但是，你已经学习了定义这些成员所需的所有知识。
// 在继续学习下一节之前，为 HasPtr 编写拷贝构造函数和拷贝赋值运算符
=======
假定我们希望 HasPtr 的行为像一个值。即，对于对象所指向的 string 成员，每个对象都有一份自己的拷贝。我们将在下一节介绍拷贝控制成员的定义。但是，你已经学习了定义这些成员所需的所有知识。在继续学习下一节之前，为 HasPtr 编写拷贝构造函数和拷贝赋值运算符
>>>>>>> 8ca32d5184cb852fee6cc29515709a7d218d67fc:c++_practice/src/c++_prime_questions_chapter_13.cpp
class HasPtr
{
public:
    HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) {}
    HasPtr(const HasPtr &hp) : ps(new std::string(*hp.ps)), i(hp.i) {}
    HasPtr &operator=(const HasPtr &hp)
    {
        auto new_p = new std::string(*hp.ps);
        delete ps;
        ps = new_p;
        i = hp.i;
        return *this;
    }
    ~HasPtr()
    {
        delete ps;
    }

private:
    std::string *ps;
    int i;
};


// 23.
// 编写时忘了一个析构函数，ps在构造函数中是动态分配的内存，所以需要进行delete

// 24.
// 如果本节的 HasPtr 版本未定义析构函数，将会发生什么？如果未定义拷贝构造函数，将会发生什么？
// 未定义析构函数，ps在使用结束后不会被合成版本的析构函数释放，造成内存泄漏。未定义拷贝构造函数，使用自定义版本的拷贝构造函数，对于ps的拷贝就会是指针本身的拷贝
// 如果未定义析构函数，将会发生内存泄漏。如果未定义拷贝构造函数，将会拷贝指针的值，指向同一个地址。


// 25.
<<<<<<< HEAD:c++_practice_题目/src/c++_prime_questions_chapter_13.cpp
// 假定希望定义 StrBlob 的类值版本，而且希望继续使用 shared_ptr，这样我们的 StrBlobPtr 类就仍能使用指向vector的 weak_ptr 了。
// 你修改后的类将需要一个拷贝的构造函数和一个拷贝赋值运算符，但不需要析构函数。解释拷贝构造函数和拷贝赋值运算符必须要做什么。解释为什么不需要析构函数
=======
假定希望定义 StrBlob 的类值版本，而且希望继续使用 shared_ptr，这样我们的 StrBlobPtr 类就仍能使用指向vector的 weak_ptr 了。你修改后的类将需要一个拷贝的构造函数和一个拷贝赋值运算符，但不需要析构函数。解释拷贝构造函数和拷贝赋值运算符必须要做什么。解释为什么不需要析构函数
>>>>>>> 8ca32d5184cb852fee6cc29515709a7d218d67fc:c++_practice/src/c++_prime_questions_chapter_13.cpp
// 动态分配的内存由shared_ptr管理，析构函数之后会自动判断进行释放，所以不需要自定义版本的析构函数

// 26.

class Hasptr1
{
public:
    //构造函数，初始化相关成员
    Hasptr1(const string &s = string()) : ps(new string(s)), i(0), use(new size_t(1)) {}

    //拷贝构造函数，将引用计数也拷贝过来，并且递增引用计数
    Hasptr1(const Hasptr1 &p) : ps(p.ps), i(p.i), use(p.use) { ++*use; }

    //拷贝赋值运算符
    Hasptr1 &operator=(const Hasptr1 &p1)
    {
        ++*p1.use;       //首先递增右侧运算符对象的引用计数
        if (--*use == 0) //递减本对象的引用计数，若没有其他用户，则释放本对象的成员
        {
            delete ps;
            delete use;
        }
        ps = p1.ps; //进行拷贝
        use = p1.use;
        i = p1.i;
        return *this;
    }

    //析构函数
    ~Hasptr1()
    {
        if (*use == 0) //引用计数变为0，说明已经没有对象再需要这块内存，进行释放内存操作
        {
            delete ps;
            delete use;
        }
    }

private:
    //定义为指针，是我们想将该string对象保存在动态内存中
    string *ps;
    size_t *use; //将计数器的引用保存
    int i;
};

// 27.

// 28.

// 29.

// 30.


// 31.




// 32.
// 类指针的 HasPtr 版本会从 swap 函数收益吗？如果会，得到了什么益处？如果不是，为什么？
// 不会。类值的版本利用swap交换指针不用进行内存分配，因此得到了性能上的提升。类指针的版本本来就不用进行内存分配，所以不会得到性能提升



// 33.



// 34.



// 35.



// 36.




// 37.




// 38.



// 39.






// 45.
// 解释左值引用和右值引用的区别？
// 常规引用被称为左值引用
// 绑定到右值的引用被称为右值引用



int main()
{

    // numbered a, b = a, c = b;
    // 	f(a);
    // 	f(b);
    // 	f(c);

    // // 默认构造函数和拷贝构造函数
    // X *px = new X; // X(), X(const X&)
    // f(*px, *px); // X(const X&)  // X(const X&)
    // delete px; // ~X()  // ~X()  // ~X()   // ~X()

    // Employee a, b, c;
    // show(a); //调用函数时需要拷贝一次
    // show(b);
    // show(c);

    // A a;
    // cout<<a.val<<endl;
    // A b = a;
    // cout<<b.val<<endl;
    // A c = b;
    // cout<<c.val<<endl;
    // show2(a); //调用函数时需要拷贝一次
    // show2(b);
    // show2(c);

    // //将A的对象当作引用或者非引用传递
    // A a(10); // 默认构造函数
    // A b(5); // 默认构造函数
    // A c(2); // 默认构造函数
    // c = a; // 拷贝复制运算符
    // show1(a);
    // show2(b);
    // show2(c);
    // //存放于容器中
    // vector<A> m;
    // m.push_back(a);
    // //动态分配
    // A *d = new A(5);
    // show2(*d);
    // delete d;

    printf("hello c++ \n");
}